﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Web;
using System.Net;
using System.IO;

namespace HHMMS.FileServer
{
    public class FileServerProvider : IFileServerProvider
    {
        #region 上传Http文件到服务器(Web)
        /// <summary>
        /// 上传文件到文件服务器后插入上传记录到数据库并返回记录ID。
        /// </summary>
        /// <param name="file">待上传的文件对象</param>
        /// <param name="folderName">保存的目录名称，如：'/Attachment/AA/'</param>
        /// <param name="attachTypeID">附件类型ID</param>
        /// <param name="creatorID">创建人ID</param>
        /// <param name="creatorName">创建人姓名</param>
        /// <param name="marks">扩展业务标记，4个</param>
        /// <returns></returns>
        public AttachmentInfo UploadToFileServer(HttpPostedFile file, string folderName, string attachTypeID, string creatorID, string creatorName, params string[] marks)
        {
            //获取文件大小
            int fileSize = file.ContentLength;

            //上传文件到文件服务器
            string filePath = this.UploadToFileServer(file, folderName, attachTypeID);

            //真实文件名
            string fileName = Path.GetFileName(file.FileName);

            //文件下载路径
            string fileUrl = string.Format("download.ashx?path={0}&name={1}", HttpUtility.UrlEncode(filePath), HttpUtility.UrlEncode(fileName));

            //上传完毕后保存上传记录到数据库
            AttachmentInfo attach = new AttachmentInfo()
            {
                CreateDate = DateTime.Now,
                FileUrl = fileUrl,
                ID = Guid.NewGuid().ToString(),
                TypeID = attachTypeID,
                CreatorID = creatorID,
                CreatorName = creatorName,
                FileName = fileName,
                FileSize = fileSize,
                FilePath = filePath,
                Mark1 = marks.Length > 0 ? marks[0] : "",
                Mark2 = marks.Length > 1 ? marks[1] : "",
                Mark3 = marks.Length > 2 ? marks[2] : "",
                Mark4 = marks.Length > 3 ? marks[3] : "",
            };
            AttachmentInfoProvider.Instance.Insert(attach);

            //上传成功则返回附件实体
            return attach;
        }

        /// <summary>
        /// 上传文件到文件服务器，返回文件在文件服务器上的相对路径。
        /// </summary>
        /// <param name="file"></param>
        /// <param name="folderName"></param>
        /// <param name="attachmentTypeID"></param>
        /// <returns></returns>
        public string UploadToFileServer(HttpPostedFile file, string folderName, string attachmentTypeID)
        {
            this.ValidateFile(file, attachmentTypeID);

            WebClient client = new WebClient();

            //设置身份认证凭据
            if (FileServerSection.Current.UseDefaultCredential)
            {
                client.UseDefaultCredentials = true;
            }
            else
            {
                NetworkCredential credentials = new NetworkCredential(FileServerSection.Current.UserName, FileServerSection.Current.Password);
                client.Credentials = credentials;
            }

            //确保文保存目录不为空
            if (string.IsNullOrEmpty(folderName))
            {
                folderName = "";
                if (!string.IsNullOrEmpty(attachmentTypeID))
                {
                    var attachType = AttachmentTypeInfoProvider.Instance.SelectSingle(attachmentTypeID);
                    if (attachType != null)
                    {
                        folderName = attachType.SaveDirName + "";
                    }
                }
            }

            //文件保存路径：folderName/yyyy/MM/dd/guid.xls
            string fileName = string.Format("{0}/{1}/{2}/{3}/{4}{5}",
                folderName.Replace("\\","/").TrimStart('\\').TrimEnd('\\'),
                DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day,
                Guid.NewGuid().ToString().ToLower(),
                Path.GetExtension(file.FileName));

            //文件服务器上接收文件的Url
            string address = string.Format("{0}/receive.ashx?fileName={1}&action=upload",
                FileServerSection.Current.Url.TrimEnd('/'),
                fileName);

            //读取并上传文件
            using (BinaryReader dr = new BinaryReader(file.InputStream))
            {
                int count = 0;
                byte[] buffer = new byte[FileServerSection.Current.BlockSize], data = null;
                while ((count = dr.Read(buffer, 0, FileServerSection.Current.BlockSize)) > 0)
                {
                    data = new byte[count];
                    Array.Copy(buffer, 0, data, 0, count);
                    client.UploadData(new Uri(address), "POST", data);
                }
                client.Dispose();
            }

            return fileName;
        }
        #endregion

        #region Http文件类型校验
        /// <summary>
        /// 校验文件的合法性，若与文件服务器配置的大小和类型不匹配则抛出异常。
        /// </summary>
        /// <param name="file"></param>
        public void ValidateFile(HttpPostedFile file)
        {
            //获取文件大小
            int fileSize = file.ContentLength;
            if (fileSize / 1024 > FileServerSection.Current.MaxFileSize)
            {
                throw new Exception("上传文件的大小不得超过文件服务器的配置的大小：" + FileServerSection.Current.MaxFileSize + "KB！");
            }

            //获取文件扩展名
            string allowExt = FileServerSection.Current.AcceptFileTypes.ToLower();
            string ext = Path.GetExtension(file.FileName).ToLower();
            if (allowExt.IndexOf(".*") == -1 && allowExt.IndexOf(ext) == -1)
            {
                throw new Exception("文件服务器不允的文件，只能上传" + allowExt + "类型的文件！");
            }
        }

        /// <summary>
        /// 校验文件的合法性，若与文件服务器配置以及附件类型对应配置的大小和类型不匹配则抛出异常。
        /// </summary>
        /// <param name="file"></param>
        /// <param name="attachmentTypeID"></param>
        public void ValidateFile(HttpPostedFile file, string attachmentTypeID)
        {
            this.ValidateFile(file);

            //若附件类型ID为空则不做附件类型校验
            if (string.IsNullOrEmpty(attachmentTypeID))
                return;

            //读取附件类型
            var attachType = AttachmentTypeInfoProvider.Instance.SelectSingle(attachmentTypeID);
            if (attachType == null)
            {
                throw new Exception("找不到附件ID为：\"" + attachmentTypeID + "\"的附件类型！");
            }

            //获取文件大小
            if ((attachType.MaxFileSize ?? 0) > 0)
            {
                int fileSize = file.ContentLength;
                if (fileSize / 1024 > attachType.MaxFileSize)
                {
                    throw new Exception("上传文件的大小不得超过【" + attachType.TypeName + "】类型附件的大小：" + attachType.MaxFileSize + "KB！");
                }
            }

            //获取文件扩展名
            if (!string.IsNullOrEmpty(attachType.AllowExt))
            {
                string allowExt = attachType.AllowExt;
                string ext = Path.GetExtension(file.FileName).ToLower();
                if (allowExt.IndexOf(".*") == -1 && allowExt.IndexOf(ext) == -1)
                {
                    throw new Exception("【" + attachType.TypeName + "】类型的附件，必须是" + allowExt + "类型的文件！");
                }
            }
        }
        #endregion

        #region 上传文件对象到服务器(Win)
        /// <summary>
        /// 上传文件到文件服务器后插入上传记录到数据库并返回记录ID。
        /// </summary>
        /// <param name="file">待上传的文件对象</param>
        /// <param name="folderName">保存的目录名称，如：'/Attachment/AA/'</param>
        /// <param name="attachTypeID">附件类型ID</param>
        /// <param name="creatorID">创建人ID</param>
        /// <param name="creatorName">创建人姓名</param>
        /// <param name="marks">扩展业务标记，4个</param>
        /// <returns></returns>
        public AttachmentInfo UploadToFileServer(FileInfo file, string folderName, string attachTypeID, string creatorID, string creatorName, params string[] marks)
        {
            //获取文件大小
            long fileSize = file.Length;

            //上传文件到文件服务器
            string filePath = this.UploadToFileServer(file, folderName, attachTypeID);

            //真实文件名
            string fileName = Path.GetFileName(file.FullName);

            //文件下载路径
            string fileUrl = string.Format("download.ashx?path={0}&name={1}", HttpUtility.UrlEncode(filePath), HttpUtility.UrlEncode(fileName));

            //上传完毕后保存上传记录到数据库
            AttachmentInfo attach = new AttachmentInfo()
            {
                CreateDate = DateTime.Now,
                FileUrl = fileUrl,
                ID = Guid.NewGuid().ToString(),
                TypeID = attachTypeID,
                CreatorID = creatorID,
                CreatorName = creatorName,
                FileName = fileName,
                FileSize = fileSize,
                FilePath = filePath,
                Mark1 = marks.Length > 0 ? marks[0] : "",
                Mark2 = marks.Length > 1 ? marks[1] : "",
                Mark3 = marks.Length > 2 ? marks[2] : "",
                Mark4 = marks.Length > 3 ? marks[3] : "",
            };
            AttachmentInfoProvider.Instance.Insert(attach);

            //上传成功则返回附件实体
            return attach;
        }

        /// <summary>
        /// 上传文件到文件服务器，返回文件在文件服务器上的相对路径。
        /// </summary>
        /// <param name="file"></param>
        /// <param name="folderName"></param>
        /// <param name="attachmentTypeID"></param>
        /// <returns></returns>
        public string UploadToFileServer(FileInfo file, string folderName, string attachmentTypeID)
        {
            this.ValidateFile(file, attachmentTypeID);

            WebClient client = new WebClient();

            //设置身份认证凭据
            if (FileServerSection.Current.UseDefaultCredential)
            {
                client.UseDefaultCredentials = true;
            }
            else
            {
                NetworkCredential credentials = new NetworkCredential(FileServerSection.Current.UserName, FileServerSection.Current.Password);
                client.Credentials = credentials;
            }

            //确保文保存目录不为空
            if (string.IsNullOrEmpty(folderName))
            {
                folderName = "";
                if (!string.IsNullOrEmpty(attachmentTypeID))
                {
                    var attachType = AttachmentTypeInfoProvider.Instance.SelectSingle(attachmentTypeID);
                    if (attachType != null)
                    {
                        folderName = attachType.SaveDirName + "";
                    }
                }
            }

            //文件保存路径：folderName/yyyy/MM/dd/guid.xls
            string fileName = string.Format("{0}/{1}/{2}/{3}/{4}{5}",
                folderName.Replace("\\", "/").TrimStart('\\').TrimEnd('\\'),
                DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day,
                Guid.NewGuid().ToString().ToLower(),
                file.Extension);

            //文件服务器上接收文件的Url
            string address = string.Format("{0}/receive.ashx?fileName={1}&action=upload",
                FileServerSection.Current.Url.TrimEnd('/'),
                fileName);

            //读取并上传文件
            using (FileStream stream = file.OpenRead())
            {
                using (BinaryReader dr = new BinaryReader(stream))
                {
                    int count = 0;
                    byte[] buffer = new byte[FileServerSection.Current.BlockSize], data = null;
                    while ((count = dr.Read(buffer, 0, FileServerSection.Current.BlockSize)) > 0)
                    {
                        data = new byte[count];
                        Array.Copy(buffer, 0, data, 0, count);
                        client.UploadData(new Uri(address), "POST", data);
                    }
                    client.Dispose();
                }
            }

            return fileName;
        }
        #endregion

        #region 文件对象类型校验
        /// <summary>
        /// 校验文件的合法性，若与文件服务器配置的大小和类型不匹配则抛出异常。
        /// </summary>
        /// <param name="file"></param>
        public void ValidateFile(FileInfo file)
        {
            //获取文件大小
            long fileSize = file.Length;
            if (fileSize / 1024 > FileServerSection.Current.MaxFileSize)
            {
                throw new Exception("上传文件的大小不得超过文件服务器的配置的大小：" + FileServerSection.Current.MaxFileSize + "KB！");
            }

            //获取文件扩展名
            string allowExt = FileServerSection.Current.AcceptFileTypes.ToLower();
            string ext = file.Extension;
            if (allowExt.IndexOf(".*") == -1 && allowExt.IndexOf(ext) == -1)
            {
                throw new Exception("文件服务器不允的文件，只能上传" + allowExt + "类型的文件！");
            }
        }

        /// <summary>
        /// 校验文件的合法性，若与文件服务器配置以及附件类型对应配置的大小和类型不匹配则抛出异常。
        /// </summary>
        /// <param name="file"></param>
        /// <param name="attachmentTypeID"></param>
        public void ValidateFile(FileInfo file, string attachmentTypeID)
        {
            this.ValidateFile(file);

            //若附件类型ID为空则不做附件类型校验
            if (string.IsNullOrEmpty(attachmentTypeID))
                return;

            //读取附件类型
            var attachType = AttachmentTypeInfoProvider.Instance.SelectSingle(attachmentTypeID);
            if (attachType == null)
            {
                throw new Exception("找不到附件ID为：\"" + attachmentTypeID + "\"的附件类型！");
            }

            //获取文件大小
            if ((attachType.MaxFileSize ?? 0) > 0)
            {
                long fileSize = file.Length;
                if (fileSize / 1024 > attachType.MaxFileSize)
                {
                    throw new Exception("上传文件的大小不得超过【" + attachType.TypeName + "】类型附件的大小：" + attachType.MaxFileSize + "KB！");
                }
            }

            //获取文件扩展名
            if (!string.IsNullOrEmpty(attachType.AllowExt))
            {
                string allowExt = attachType.AllowExt;
                string ext = file.Extension;
                if (allowExt.IndexOf(".*") == -1 && allowExt.IndexOf(ext) == -1)
                {
                    throw new Exception("【" + attachType.TypeName + "】类型的附件，必须是" + allowExt + "类型的文件！");
                }
            }
        }
        #endregion

        #region 从服务器删除文件
        /// <summary>
        /// 根据文件ID删除指定数据库文件的记录并删除对应的文件。
        /// </summary>
        /// <param name="fileID"></param>
        public void DeleteFile(string fileID)
        {
            //获取文件详细信息
            AttachmentInfo file = AttachmentInfoProvider.Instance.SelectSingle(fileID);
            if (file == null)
                return;

            //文件服务器上接收文件的Url
            string address = string.Format("{0}/receive.ashx?fileName={1}&action=delete",
                FileServerSection.Current.Url.TrimEnd('/'),
                file.FileUrl.ToLower().Replace("download.ashx?path=", ""));

            //发送文件删除请求
            using (WebClient client = new WebClient())
            {
                client.UploadData(address, new byte[0]);
            }

            //删除数据库记录
            AttachmentInfoProvider.Instance.Delete(fileID);
        }
        #endregion
    }
}
